from tornado.websocket import WebSocketHandler
from tornado.ioloop import  IOLoop
import tornado.web
import tornado.ioloop
import tornado.httpserver
import struct
import paramiko
import threading
import time
import os
import json
from api.common.util import SingletonDBPool
from api.models.cmdb import Record
import requests,os
dir_path = os.path.dirname(os.path.abspath(__file__))
from concurrent.futures import ThreadPoolExecutor

try:
    from json.decoder import JSONDecodeError
except ImportError:
    JSONDecodeError = ValueError

executor = ThreadPoolExecutor(max_workers=2*5)


class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.write("Hello,World")

class RecordHandler(tornado.web.RequestHandler):
    def get(self):
        id = self.get_argument("id")
        pool = SingletonDBPool()
        session = pool.connect()
        result = session.query(Record).filter(Record.id==id).first()
        if result:
            download_url = result.download_url
            filename = result.filename
            type = None
            if download_url:
                myfile = requests.get(download_url)
                filepath = os.path.join(dir_path, "static","download",filename)
                open(filepath, 'wb').write(myfile.content)
                type = "remote"
            else:
                type= "local"
            return self.render("record.html", filename=filename,type=type)
        else:
            self.write(json.dumps({"fail":"记录的文件数据丢失"}))

# 这里应该用多进程的方式
class MyThread(threading.Thread):
    def __init__(self, id,websock, ssh,chan,filename,start_time,dst_addr=None):
        threading.Thread.__init__(self)
        self.websock =websock
        self.ssh = ssh
        self.chan=chan
        self.filename=filename
        self.start_time =start_time
        self.id = id

    def run(self):
        print("线程running")
        while not self.chan.exit_status_ready():
            # time.sleep(0.1)
            try:
                data = self.chan.recv(32*1024)
                if data:
                    self.websock.write_message(data)
                    with open(self.filename, 'a', buffering=1) as fd:
                        dmsg = data.decode('utf-8')
                        iodata = [time.time() - self.start_time, 'o', f'{dmsg}']  # 构造格式
                        fd.write(json.dumps(iodata) + '\n')  # 写进文件
                else:
                    print("无数据")
            except Exception as e:
                pass
                # print(e)
                # print("id:{} write message 异常: ".format(self.id),e)
        try:
            self.chan.close()
            self.ssh.close()
        except Exception as e:
            print("线程里关闭channel/ssh conn异常:%r" % e)
        return False


class EchoWebSocket(tornado.websocket.WebSocketHandler):
    def open(self):
        print("WebSocket opened")
        print(self.request.body)
        hostname = self.get_argument('hostname')
        port = int(self.get_argument('port'))
        username = self.get_argument('username')
        password = self.get_argument('password')
        id = int(self.get_argument('id'))
        print(id,hostname,port)
        self.server_open = False

        client = paramiko.SSHClient()
        client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        try:
            client.connect(hostname=hostname,port=port,username=username,password=password,timeout=2)
        except Exception as e:
            print("{} {} 连接异常".format(hostname,id),e)
            # os._exit(-1)
            return
        channel = client.invoke_shell()
        # 设置超时，会导致一直触发on_message
        # channel.settimeout(1)

        try:
            record_dir = os.path.join(dir_path,'static','record')
            if not os.path.isdir(record_dir):
                os.makedirs(record_dir)
            starttime = time.strftime("%Y%m%d_%H%M%S")
            filename = hostname + '_'+username+'_'+starttime+".cast"
            filepath = os.path.join(record_dir,filename)

            date = time.time()
            header = {
                "version": 2,
                "width": 160,
                "height": 40,
                "timestamp": date,
                "env": {"SHELL": "/bin/bash",
                        "TERM": "xterm-256color"},
                "title": "video"
            }
            with open(filepath, 'w') as fd:
                fd.write(json.dumps(header) + '\n')
            for i in range(2):
                msg = channel.recv(32*1024)
                self.write_message(msg)
                with open(filepath, 'a') as fd:
                    dmsg = msg.decode('utf-8')
                    iodata = [time.time() - date, 'o', f'{dmsg}']
                    fd.write(json.dumps(iodata)+'\n')

            self.ssh = client
            self.chan = channel
            self.hostname = hostname
            self.username = username
            self.start_time = starttime
            self.filename = filename
            self.filepath = filepath
            self.id=id
            self.server_open = True

            t1 = MyThread(id,self,client,channel,filepath,date)
            t1.setDaemon(True)
            t1.start()
        except Exception as e:
            print("ssh连接建立,写header出错")


    def on_message(self, message):
        if self.server_open:
            # print("websock收到的消息:"+str(message))
            try:
                msg = json.loads(message)
                # print(msg)
            except JSONDecodeError:
                return

            if not isinstance(msg, dict):
                return

            operate = msg.get('operate')
            if operate == "resize":
                cols = msg.get('cols')
                rows = msg.get('rows')
                print(cols,rows)
                try:
                    self.chan.resize_pty(width=cols,height=rows)
                except (TypeError, struct.error, paramiko.SSHException):
                    print(struct.error)
                    pass
            elif operate == "command":
                message  = msg.get('command')
                try:
                    self.chan.send(message)
                except Exception as e:
                    print("{} {} on_message:".format(self.hostname,self.id),e)
            else:
                pass

    def on_close(self):

        if self.server_open:
            print("{} {} WebSocket closed".format(self.hostname, self.id))
            try:
                download_url = None
                pool = SingletonDBPool()
                session = pool.connect()
                end_time =  time.strftime("%Y%m%d_%H%M%S")
                record = Record(host=self.hostname,username=self.username,filename=self.filename,
                                download_url=download_url,start_time=self.start_time,end_time=end_time)
                session.add(record)
                session.commit()
            except Exception as e:
                print("异常:%r" % e)
                print("插入record数据异常")

    def check_origin(self, origin):
        return True

def make_app():
    return tornado.web.Application(
        [
            (r"/",MainHandler),
            (r"/record", RecordHandler),
            (r"/websocket", EchoWebSocket),
        ],
        static_path=os.path.join(os.path.dirname(__file__), "static"),
        template_path=os.path.join(os.path.dirname(__file__), "templates"),
    )

if __name__ == "__main__":
    app = make_app()
    app.listen(7777)
    IOLoop.current().start()
    # http_server = tornado.httpserver.HTTPServer(app)
    # http_server.bind(7777)
    # http_server.start(3)
    # IOLoop.instance().start()